package com.sinodata.bsm.center.service.resource;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.sinodata.bsm.center.bean.Condition;
import com.sinodata.bsm.center.bean.Page;
import com.sinodata.bsm.center.bean.RelationHelper;
import com.sinodata.bsm.center.bean.ResBean;
import com.sinodata.bsm.center.cache.NewEventCache;
import com.sinodata.bsm.center.cache.RelationTypeCache;
import com.sinodata.bsm.center.cache.ResCache;
import com.sinodata.bsm.center.cache.ResFindDefCache;
import com.sinodata.bsm.center.cache.ResRelationCache;
import com.sinodata.bsm.center.cache.ResTypeCache;
import com.sinodata.bsm.center.cache.ResViewRelCache;
import com.sinodata.bsm.center.dao.BaseDao;
import com.sinodata.bsm.center.dao.impl.ResAttrValHisDaoImpl;
import com.sinodata.bsm.center.dao.impl.ResDaoImpl;
import com.sinodata.bsm.center.engine.probe.ProbeStatusManager;
import com.sinodata.bsm.center.engine.resource.health.ResHealthEngine;
import com.sinodata.bsm.center.message.ProbeMessenger;
import com.sinodata.bsm.center.service.event.EventRuleService;
import com.sinodata.bsm.center.service.event.EventService;
import com.sinodata.bsm.center.service.relation.ResRelationService;
import com.sinodata.bsm.center.util.spring.SpringContextHolder;
import com.sinodata.bsm.common.constants.EventConstants;
import com.sinodata.bsm.common.constants.RelationConstants;
import com.sinodata.bsm.common.vo.Event;
import com.sinodata.bsm.common.vo.RelationType;
import com.sinodata.bsm.common.vo.Res;
import com.sinodata.bsm.common.vo.ResAttrVal;
import com.sinodata.bsm.common.vo.ResFindDef;
import com.sinodata.bsm.common.vo.ResRelation;
import com.sinodata.bsm.common.vo.ResType;
import com.sinodata.bsm.common.vo.ResViewRel;

/**
 * <p>
 * Description:
 * </p>
 * 
 * @author liulibo
 * @version 1.0
 * 
 * <p>
 * History:
 * 
 * Date Author Version Description
 * ---------------------------------------------------------------------------------
 * 2012-5-7 下午4:06:29 liulibo 1.0 To create
 * </p>
 * 
 * @since
 * @see
 */
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ResService {
    @Autowired
    private ResCache resCache;
    @Autowired
    private AttributeService attributeService;
    @Autowired
    private BaseDao baseDao;
    @Autowired
    private ResRelationService resRelationService;

    @Autowired
    private ResRelationCache resRelationCache;
    @Autowired
    private RelationTypeCache relationTypeCache;
    @Autowired
    private ResDaoImpl resDaoImpl;
    @Autowired
    private EventService eventService;

    public Res get(Long id) {
        return resCache.get(id);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addResource(Long parentId, Res res, List<ResAttrVal> attValList, Long relationTypeId) {
        res.setDeleted(0);
        res.setHealthDegree(100);
        baseDao.save(res);
        ResRelation resRelation = new ResRelation();
        resRelation.setStartId(parentId);
        resRelation.setEndId(res.getId());
        resRelation.setRelationTypeId(relationTypeId);
        resRelationService.addResRelation(resRelation);
        for (ResAttrVal attval : attValList) {
            attval.setResId(res.getId());
            attval.setTime(new Date());
            attributeService.saveResAttrVal(attval);
        }
        resCache.put(res.getId(), res);
        ProbeMessenger.notifyResAdd(res, attValList, resRelation);
        // 最后刷新资源状态
        SpringContextHolder.getBean(ResHealthService.class).addResHealthStatus(res);
        ResHealthEngine.getInstance().refreshResFromParent(ResBean.get(parentId).res());
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addTopResource(Long viewId, Res res, List<ResAttrVal> attValList) {
        res.setDeleted(0);
        res.setHealthDegree(100);
        baseDao.save(res);
        for (ResAttrVal attval : attValList) {
            attval.setResId(res.getId());
            attval.setTime(new Date());
            attributeService.saveResAttrVal(attval);
        }
        resCache.put(res.getId(), res);
        if (viewId != null) {
            ResViewRel viewRel = new ResViewRel();
            viewRel.setResId(res.getId());
            viewRel.setResViewId(viewId);
            SpringContextHolder.getBean(ResViewService.class).saveResViewRel(viewRel);
        }
        SpringContextHolder.getBean(ResHealthService.class).addResHealthStatus(res);
        ProbeMessenger.notifyResAdd(res, attValList, new ResRelation());
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateResource(Res res, List<ResAttrVal> attValList, ResRelation newResRelation) {
        baseDao.update(res);
        for (ResAttrVal attval : attValList) {
            attval.setResId(res.getId());
            attval.setTime(new Date());
            attributeService.saveResAttrVal(attval);
        }

        resCache.put(res.getId(), res);
        if (newResRelation != null) {
            List<ResRelation> relList = resRelationService.getResRelationsOfRes(res.getId());
            if (relList != null) {
                for (int j = relList.size() - 1; j >= 0; j--) {
                    ResRelation rel = relList.get(j);
                    if (rel.getStartId().longValue() == newResRelation.getStartId() && rel.getEndId().longValue() == res.getId()) {
                        resRelationService.removeResRelation(rel);
                        break;
                    }
                }
            }
            resRelationService.addResRelation(newResRelation);
        }

        ProbeMessenger.notifyResUpdate(res, attValList, newResRelation);

    }

    /**
     * 删除一个资源
     * 
     * @param resId
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteResource(Long parentId, Long resId) {
        //如果有多个父资源，则只删除父资源与本资源的关系即可
        List<ResBean> parents = RelationHelper.parents(resId);
        boolean isDeleted = parents.size() > 1 ? false : true;
        if (!isDeleted) {
            //只删除资源的关系
            List<ResRelation> relList = resRelationCache.getResRelationsOfRes(resId);
            ResRelation relation = null;
            for (ResRelation rel : relList) {
                if (rel.getStartId().longValue() == parentId && rel.getEndId().longValue() == resId) {
                    relation = rel;
                    break;
                }
            }
            resRelationService.removeResRelation(relation);
        } else {
            //只有一层关系，进行删除资源
            recursionDeleteRes(resId);
        }
        ResHealthEngine.getInstance().refreshResFromParent(ResBean.get(parentId).res());
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteTopResource(Long resViewId, Long resId) {
        List<ResBean> parents = RelationHelper.parents(resId);
        //因为如果是顶级节点，他在其它视图或在此视图的其它平级资源下可能存在。
        boolean isDeleted = parents.size() > 0 ? false : true;
        List<ResViewRel> allRels = SpringContextHolder.getBean(ResViewRelCache.class).getResViewRelByResId(resId);
        if (allRels.size() > 1) {
            isDeleted = false;
        }
        if (isDeleted) {
            recursionDeleteRes(resId);
        }
        //删除连线
        List<ResViewRel> rellist = SpringContextHolder.getBean(ResViewService.class).getResViewRelByViewId(resViewId);
        ResViewRel deleteRel = null;
        if (rellist != null)
            for (ResViewRel rel : rellist) {
                if (rel.getResId() == resId.longValue()) {
                    deleteRel = rel;
                    break;
                }
            }
        if (deleteRel != null) {
            SpringContextHolder.getBean(ResViewService.class).deleteResViewRel(deleteRel);
        }
    }

    /**
     * 递归删除资源
     * @param resId
     */
    private void recursionDeleteRes(Long resId) {
        List<ResBean> children = RelationHelper.children(resId);
        for (ResBean bean : children) {
            deleteResource(resId, bean.res().getId());
        }
        deleteChildRes(resId);
    }

    /**
     * 待完善,后续需删除 删除一个叶子资源
     * 
     * @param child
     */
    private void deleteChildRes(Long resId) {
        List<Event> events = SpringContextHolder.getBean(NewEventCache.class).getNewEventsByResId(resId);
        if (events != null) {
            Long[] eventIds = new Long[events.size()];
            for (int i = events.size() - 1; i >= 0; i--) {
                eventIds[i] = events.get(i).getId();
            }
            eventService.batchConfirm(eventIds, EventConstants.CONFIRM_TYPE_USER, "admin", null, null, true);
        }
        List<ResAttrVal> attValList = attributeService.getResAttrValByResId(resId);
        // 1、删除资源属性
        for (int i = attValList.size() - 1; i >= 0; i--) {
            attributeService.deleteResAttrVal(attValList.get(i));
        }

        //        SpringContextHolder.getb
        // 删除属性历史值
        SpringContextHolder.getBean(ResAttrValHisDaoImpl.class).deleteByResId(resId);
        // 2、删除所有关联资源的关系
        resRelationService.removeResRelationByResId(resId);
        // //////////////////////////////////////////////////////
        // 删除各关联表的数据写在此类，仿写 采集任务
        // 删除自定义采集任务
        SpringContextHolder.getBean(CollectTaskService.class).deleteCollectTaskByResId(resId);
        // 删除事件规则
        SpringContextHolder.getBean(EventRuleService.class).deleteByResId(resId);
        //删除别名
        SpringContextHolder.getBean(PropertyDisplayService.class).delByResId(resId);

        // /////////////////////////////////////////////////////////
        Res res = get(resId);
        res.setDeleted(1);
        baseDao.update(res);
        ProbeMessenger.notifyResDelete(res);
        SpringContextHolder.getBean(ResViewService.class).deleteResViewRel(resId);
        resCache.remove(res.getId());
    }

    /**
     * 删除最末资源，即如果该资源下有子资源则不删除该资源
     * @param ids
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteByIsChildRes(Long[] ids) {
        for (Long resId : ids) {
            List<ResBean> childrenRes = RelationHelper.children(resId);
            if (childrenRes.size() > 0) {
                continue;
            }
            deleteChildRes(resId);
        }
    }

    public List<ResBean> findChildren(Long parenResId, Long resTypeId) {
        List<ResRelation> relations = resRelationCache.getResRelationsOfRes(parenResId);
        List<ResBean> resources = new ArrayList<ResBean>();
        if (relations != null) {
            Set<RelationType> relationTypes = relationTypeCache.getSelfAndSubRelationType(RelationConstants.FILIATION);
            Long[] relationTypeIds = new Long[relationTypes.size()];
            int i = 0;
            for (RelationType relationType : relationTypes) {
                relationTypeIds[i] = relationType.getId();
                i++;
            }
            for (ResRelation resRelation : relations) {
                if (resRelation.getStartId().longValue() == parenResId.longValue()) {
                    boolean isRel = false;
                    for (int j = 0; j < relationTypeIds.length; j++) {
                        if (relationTypeIds[j].longValue() == resRelation.getRelationTypeId().longValue()) {
                            isRel = true;
                        }
                    }
                    if (!isRel) {
                        continue;
                    }
                    ResBean res = ResBean.get(resRelation.getEndId());
                    if (res != null && !resources.contains(res) && res.res().getResTypeId().longValue() == resTypeId) {
                        resources.add(res);
                    }
                }
            }
        }
        return resources;
    }

    /**
     * 判断是否为可被发现的资源
     * 
     * @param resId
     * @return
     */
    public boolean canSelfFindRes(Long resId) {
        Res res = resCache.get(resId);
        ResType resType = (SpringContextHolder.getBean(ResTypeCache.class)).get(res.getResTypeId());
        if (resType.getFoundFlag() == null || resType.getFoundFlag() == 0) {
            return false;
        }
        List<ResFindDef> resFindDef = (SpringContextHolder.getBean(ResFindDefCache.class)).getIncludeForResType(res.getResTypeId().longValue());
        if (resFindDef == null || resFindDef.size() == 0) {
            return false;
        }
        return true;
    }

    /**
     * IP变更
     * 
     * @param resIds
     * @param newIp
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void changeResIP(List<Long> resIds, String newIp) {
        Map<Long, List<Res>> map = new HashMap<Long, List<Res>>();
        for (Long resId : resIds) {
            Res res = resCache.get(resId);
            if (res == null) {
                continue;
            }
            res.setIp(newIp);
            baseDao.update(res);
            List<Res> group = null;
            if (map.containsKey(res.getProbeId().longValue())) {
                group = map.get(res.getProbeId().longValue());
            } else {
                group = new ArrayList<Res>();
                map.put(res.getProbeId().longValue(), group);
            }
            group.add(res);
        }
        for (Long probeId : map.keySet()) {
            List<Res> resList = map.get(probeId);
            ArrayList<Object[]> list = new ArrayList<Object[]>();
            for (Res res : resList) {
                Object[] objs = new Object[3];
                objs[0] = res;
                objs[1] = new ArrayList<ResAttrVal>(0);
                objs[2] = new ResRelation();
                list.add(objs);
            }
            ProbeMessenger.notifyBatchResUpdate(probeId, list);
        }

    }

    /**
     * Probe变更
     * 
     * @param resIds
     * @param newIp
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void changeResProbeId(List<Long> resIds, Long newProbeId) {
        Set<Long> probleIds = new HashSet<Long>();
        for (Long resId : resIds) {
            Res res = resCache.get(resId);
            if (res == null) {
                continue;
            }
            probleIds.add(res.getProbeId().longValue());
            res.setProbeId(newProbeId);
            baseDao.update(res);

        }
        for (Long val : probleIds) {
            ProbeStatusManager.getInstance().stopProbe(val);
        }
        ProbeStatusManager.getInstance().stopProbe(newProbeId);
    }

    /**
     * 资源应用的查询
     * 
     * @param page
     * @param condition
     * @return
     */
    public List<Res> findByPage(Page<Res> page, Condition condition) {
        return resDaoImpl.findByPage(page, condition);
    }

    /**
     * 事件管理-资源查询
     * 
     * @param page
     * @param condition
     * @return
     */
    public List<Res> search(Page page, Condition condition, String dimension) {
        List<Res> res = new ArrayList<Res>();
        String resTypeId1 = "";
        List<String> dimensionList = new ArrayList<String>();
        if (!dimension.equals("")) {
            String[] array = dimension.split("[|]");
            for (String scope : array) {
                if (scope.split(",")[0].equals("0-资源类别")) {
                    resTypeId1 = scope.split(",")[1];
                } else {
                    dimensionList.add(scope.split(",")[1]);
                }
            }
        }
        Page p = resDaoImpl.search(page, condition, resTypeId1, dimensionList);
        List<Object> list = p.getRows();
        int r = 0;
        for (int i = 0; i < list.size(); i++) {
            Object[] ob = (Object[]) list.get(i);

            Res entity = new Res();
            Object id = ob[r++];
            Object resTypeId = ob[r++];
            Object ip = ob[r++];
            Object name = ob[r++];
            Object desc = ob[r++];
            Object paused = ob[r++];
            Object probeId = ob[r++];
            Object healthStatus = ob[r++];

            entity.setId(Long.parseLong(id.toString()));
            entity.setResTypeId(Long.parseLong(resTypeId.toString()));
            entity.setIp(ip == null ? "" : ip.toString());
            entity.setName(name == null ? "" : name.toString());
            entity.setDesc(desc == null ? "" : desc.toString());
            entity.setPaused(Integer.parseInt(paused.toString()));
            entity.setProbeId(Long.parseLong(probeId.toString()));
            entity.setHealthStatus(Integer.parseInt(healthStatus.toString()));
            res.add(entity);

            r = 0;
        }
        return res;
    }

    public List<Res> findAll() {
        return resCache.findAll();
    }

    /**
     * 查询一个节点的所有父节点
     * 
     * @param
     * @return
     */
    public List<Long> findParentIdsByResId(Long resId) {
        List<Object> list = resDaoImpl.findParentIdsByResId(resId);
        List<Long> returnList = new ArrayList<Long>();
        for (int i = 0; i < list.size(); i++) {
            Long id = Long.parseLong(list.get(i).toString());
            returnList.add(id);
        }
        return returnList;
    }

    public List<Long> findResIdByMonitorType(Long monitorTypeId) {
        List<BigDecimal> list = resDaoImpl.findResIdByMonitorType(monitorTypeId);
        List<Long> results = new ArrayList<Long>();
        for (int i = 0; list != null && i < list.size(); i++) {
            results.add(list.get(i).longValue());
        }
        return results;
    }

    /**
     * 批量添加资源    用于集成
     * @param resList
     * @throws Exception
     */
    public void batchAddRes(Long parentId, List<Res> resList, List<ResAttrVal> attValList, Long relId) throws Exception {
        for (Res res : resList) {
            addResource(parentId, res, attValList, RelationConstants.SUPPORT);
        }
    }

    /**
     * 完全删除一个资源
     * 假设资源有4个父资源
     * 1 先删除三个关系
     * 2 再把此资源完全删除
     * 3 更新每一个父资源的健康度
     * @param resId
     */
    public void deleteResAll(Long resId) {
        //        List<ResBean> parents = RelationHelper.parents(resId);
        //        //删除资源的所有关系
        //        resRelationService.removeResRelationByResId(resId);
        //        //递归删除这个资源及其子资源
        //        recursionDeleteRes(resId);
        //        //刷新所有父资源的状态
        //        for (ResBean bean : parents) {
        //            ResHealthEngine.getInstance().refreshResFromParent(bean.res());
        //        }
        //        recursionDeleteRes(resId);

        List<ResBean> parents = RelationHelper.parents(resId);
        List<ResRelation> relList = resRelationCache.getResRelationsOfRes(resId);
        if (parents.size() > 1) {
            for (int i = 0; i < parents.size(); i++) {
                Res res = parents.get(i).res();
                if (i != parents.size() - 1) {
                    ResRelation relation = null;
                    for (ResRelation rel : relList) {
                        if (rel.getStartId().longValue() == res.getId() && rel.getEndId().longValue() == resId) {
                            relation = rel;
                            break;
                        }
                    }
                    resRelationService.removeResRelation(relation);
                } else {
                    recursionDeleteRes(resId);
                }
                ResHealthEngine.getInstance().refreshResFromParent(res);
            }
        } else {
            recursionDeleteRes(resId);
        }
    }

    /**
     * 通过资源类别ID查询资源，包含子资源类别的资源
     * 
     * @param resTypeId
     * @return
     */
    public List<Res> findByResTypeId(Long resTypeId) {

        List<Res> list = resDaoImpl.findByResTypeId(resTypeId);
        if (list == null) {
            list = new ArrayList<Res>(0);
        }
        return list;
    }

    /**
     * 从预存的property中获得没有被占用的Id为最小的一个
     * @param DateTypeId
     * @param ResTypeId
     * @return
     */
    public Long getMyPropertyIdByDateTypeIdResTypeId(Integer dateTypeId, Long resTypeId, Long resId) {
        return resDaoImpl.getMyPropertyIdByDateTypeIdResTypeId(dateTypeId, resTypeId, resId);
    }

    /**
     * 查找没有资源关系的游离资源
     * 
     * @return
     */
    public List<Long> findNoRelationResId() {
        List<BigDecimal> list = resDaoImpl.findNoRelationResId();
        List<Long> results = new ArrayList<Long>();
        for (int i = 0; list != null && i < list.size(); i++) {
            results.add(list.get(i).longValue());
        }
        return results;
    }

    /**
     * 根据父资源ID,查找按资源类别分组显示子资源
     * @param parentResId
     * @return
     */
    public List<List<Res>> children(Long parentResId) {
        Set<RelationType> relationTypes = relationTypeCache.getSelfAndSubRelationType(RelationConstants.FILIATION);
        Set<Long> relationTypesIds = new HashSet<Long>();
        for (RelationType relationType : relationTypes) {
            relationTypesIds.add(relationType.getId());
        }
        List<ResRelation> relations = resRelationCache.getResRelationsOfRes(parentResId);
        List<Res> children = new ArrayList<Res>();
        if (relations != null) {
            Res child = null;
            for (ResRelation resRelation : relations) {
                if (resRelation.getStartId().longValue() == parentResId.longValue() && relationTypesIds.contains(resRelation.getRelationTypeId().longValue())) {
                    if ((child = get(resRelation.getEndId())) != null) {
                        children.add(child);
                    }

                }
            }
        }
        List<List<Res>> types = new ArrayList<List<Res>>();
        for (Res child : children) {
            List<Res> type = null;
            for (int j = 0; j < types.size(); j++) {
                if (types.get(j).get(0).getResTypeId().longValue() == child.getResTypeId().longValue()) {
                    type = types.get(j);
                    break;
                }
            }
            if (type == null) {
                type = new ArrayList<Res>();
                types.add(type);
            }
            type.add(child);
        }
        return types;
    }
}
